using System;
using System.Activities;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Crm.Sdk.Messages;

namespace CRM2011WorkflowUtilities
{
    public class Unshare : CodeActivity
    {
        protected override void Execute(CodeActivityContext executionContext)
        {
            try
            {
                //Create the context and tracing service
                IExecutionContext context = executionContext.GetExtension<IExecutionContext>();
                IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
                ITracingService tracer = executionContext.GetExtension<ITracingService>();

                EntityReference callingUser = new EntityReference("systemuser", context.UserId);

                EntityReference user = User.Get(executionContext);
                EntityReference team = Team.Get(executionContext);

                if (PrimaryEntity.Get(executionContext))
                {
                    tracer.Trace("Unsharing process primary entity");
                    UnshareWithUserTeam(new EntityReference(context.PrimaryEntityName, context.PrimaryEntityId), user, team, callingUser, service);
                }
                else
                {
                    string relatedAttribute = RelatedAttributeName.Get(executionContext);
                    if (string.IsNullOrEmpty(relatedAttribute))
                    {
                        Helpers.Throw("If unsharing related ent)ty, related attribute name must be specified in the process unshare step configuration");
                    }

                    // Retrieve primary entity with the required attribute
                    tracer.Trace("Retrieving process primary entity");
                    Entity primaryEntity = service.Retrieve(context.PrimaryEntityName, context.PrimaryEntityId, new ColumnSet(relatedAttribute));

                    if (primaryEntity.Contains(relatedAttribute))
                    {
                        EntityReference reference = primaryEntity[relatedAttribute] as EntityReference;
                        if (reference == null)
                        {
                            Helpers.Throw(string.Format("The attribute {0} on entity {1} is expected to be of EntityReference type",
                                relatedAttribute, context.PrimaryEntityName));
                        }

                        tracer.Trace("Unsharing entity related to primary entity by attribute " + relatedAttribute);
                        UnshareWithUserTeam(reference, user, team, callingUser, service);
                    }
                }

            }
            catch (Exception ex)
            {
               Helpers.Throw(String.Format("An error occurred in the {0} plug-in.",
                       this.GetType().ToString()),
                     ex);
            }

            return;
        }

        private void UnshareWithUserTeam(EntityReference target, EntityReference user, EntityReference team, EntityReference callingUser, IOrganizationService service)
        {
            if (user != null)
            {
                UnshareRecord(target, user, service);
            }
            if (team != null)
            {
                UnshareRecord(target, team, service);
            }
            if (user == null && team == null)
            {
                // Unshare with all principals
                RetrieveSharedPrincipalsAndAccessRequest r = new RetrieveSharedPrincipalsAndAccessRequest
                {
                    Target = target,
                };
                RetrieveSharedPrincipalsAndAccessResponse resp = (RetrieveSharedPrincipalsAndAccessResponse)service.Execute(r);
                foreach (PrincipalAccess pa in resp.PrincipalAccesses)
                {
                    EntityReference principal = pa.Principal;
                    if (!principal.LogicalName.Equals("systemuser", StringComparison.InvariantCultureIgnoreCase))
                    {
                        // There is a bug in which the logical name is "user" instead of "team" for team principals.
                        principal.LogicalName = "team";
                    }
                    UnshareRecord(target, principal, service);
                };
            }
        }

        private void UnshareRecord(EntityReference target, EntityReference principal, IOrganizationService service)
        {
            RevokeAccessRequest revokeRequest = new RevokeAccessRequest()
            {
                Target = target,
                Revokee = principal,
            };
            service.Execute(revokeRequest);
        }

        #region Input Parameters

        [RequiredArgument]
        [Input("Unshare primary entity (True) or related entity (False)")]
        public InArgument<bool> PrimaryEntity { get; set; }

        [Input("If unsharing related entity, specify attribute name to get the related entity")]
        public InArgument<string> RelatedAttributeName { get; set; }

        [Input("User to unshare the record with")]
        [ReferenceTarget("systemuser")]
        public InArgument<EntityReference> User { get; set; }

        [Input("Team to unshare the record with")]
        [ReferenceTarget("team")]
        public InArgument<EntityReference> Team { get; set; }

        #endregion

        #region Output Parameters
        #endregion
    }
}
